#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include <cassert>

#define PROCESS_NUM 5
#define MAKE_SRAND() srand((unsigned int)time(0))
// 目的：想创建5个匿名管道，父进程想管道发送固定的整数，让5个子进程做不同的任务

/////////////////////////////////////////////子进程要完成某种任务///////////////////////////////////////////////
// 函数指针 类型
typedef void (*func_t)();

void downLoadTask()
{
    std::cout << getpid() << "下载任务\n"
              << std::endl;
    sleep(1);
}

void ioTask()
{
    std::cout << getpid() << "IO任务\n"
              << std::endl;
    sleep(1);
}

void flushTask()
{
    std::cout << getpid() << "刷新任务\n"
              << std::endl;
    sleep(1);
}

void loadTaskFunc(std::vector<func_t> *out)
{
    assert(out);
    out->push_back(downLoadTask);
    out->push_back(ioTask);
    out->push_back(flushTask);
}
//////////////////////////////////////////////下面代码是一个多进程程序///////////////////////////////////////////
class subEp // Endpoint : 有另一端的意思
{
public:
    subEp(pid_t subId, int writeFd)
        : _subId(subId), _writeFd(writeFd)
    {
        char nameBuffer[1024];
        snprintf(nameBuffer, sizeof nameBuffer, "process-%d[pid(%d)-fd(%d)]", number++, _subId, _writeFd);
        _name = nameBuffer;
    }

public:
    static int number;
    std::string _name;
    pid_t _subId;
    int _writeFd;
};

int subEp::number = 0;

int recvTask(int readFd)
{
    int code = 0;
    ssize_t s = read(readFd, &code, sizeof code);
    if (s == 4)
        return code;
    else if (s <= 0)
        return -1;
    else
        return 0;
}

void creatSubProcess(std::vector<subEp> *subs, std::vector<func_t> &funcMap)
{
    std::vector<int> deleteFd;
    for (int i = 0; i < PROCESS_NUM; ++i)
    {
        int fds[2];
        int n = pipe(fds);
        assert(n == 0);
        (void)n; // 简单把n用一下，如用弄了一个变量并没有被使用，编译器会有警告。
        pid_t id = fork();
        if (id == 0)
        {
            for(int i = 0;i < deleteFd.size();i++) close(deleteFd[i]);//关闭的是上一层的读端

            // 子进程,进行处理任务
            close(fds[1]);
            while (true) // 子进程就不退出了
            {
                // 1.或者命令码，如果没有发送，我们子进程应该阻塞
                int CommandCode = recvTask(fds[0]);
                // 2.完成任务
                if (CommandCode >= 0 && CommandCode < funcMap.size()) // 命令码有可能为0
                {
                    // funcMap[CommandCode];  err! 这只是表示函数名
                    funcMap[CommandCode](); // 这才是调用！
                }
                else if (CommandCode == -1)
                    break;
            }
            exit(0);
        }
        // 父进程关闭读
        close(fds[0]);
        subEp sub(id, fds[1]);           // id是子进程pid
        subs->push_back(std::move(sub)); // 右值引用
        deleteFd.push_back(fds[1]);
    }
}

// void makeRandSeek()//make随机数种子
// {

// }

void sendTask(const subEp &process, int taskNum)
{
    std::cout << "send task num: " << taskNum << "send to: " << process._name << std::endl;
    ssize_t n = write(process._writeFd, &taskNum, sizeof taskNum);
    assert(n == sizeof(int)); // 一定要保证是4个字节
    (void)n;
}

void loadBlancdContrl(std::vector<subEp> &subs, std::vector<func_t> &funcMap, int count)
{
    int processnum = subs.size(); // 子进程数量
    int tasknum = funcMap.size();
    bool forever = (count == 0 ? true : false);
    while (true)
    {
        // 1.选择一个子进程 --> std::vector<subEp> -> index
        // 我们将任务均衡的下发给每一个进程，让子进程进行：负载均衡 -- 单机版
        int sub_idx = rand() % processnum;
        // 2.选择一个任务  --> std::vector<func_t> -> index
        int task_idx = rand() % tasknum;
        // 3.任务发送给选择的进程
        sendTask(subs[sub_idx], task_idx);
        sleep(1);
        if (!forever)
        {
            count--;
            if (count == 0)
                break;
        }
    }
    // write quit -> read 0
    for (int i = 0; i < processnum; i++)
    {
        close(subs[i]._writeFd);
    }
}

void waitProcess(std::vector<subEp>  processes)
{
    int processnum = processes.size();
    for (int i = 0; i < processnum; i++)
    {
        waitpid(processes[i]._subId,nullptr,0);//如果是这样，回收资源是倒着回收的
        std::cout << "wait sub process success ..." << processes[i]._subId << std::endl;
    }
}

int main()
{
    MAKE_SRAND();
    // 1.建立子进程并建立喝子进程通信的管道，有bug
    // 1.1加载方法表
    std::vector<func_t> funcMap; // 想弄一个函数指针的表
    std::vector<subEp> subs;     // 子进程管理起来
    // 1.2创建子进程，并且维护好父子通信信道
    loadTaskFunc(&funcMap); // 把三个任务加载进来
    creatSubProcess(&subs, funcMap);

    // 2.父进程，控制子进程
    int taskCnt = 3;                          // -1:永远进行
    loadBlancdContrl(subs, funcMap, taskCnt); // 负载均衡式控制

    // 3.回收子进程信息
    waitProcess(subs);

    return 0;
}